<template>
  <Tooltip placement="top">
    <template #title>
      <span>{{ t('component.table.settingColumn') }}</span>
    </template>
    <Popover
      placement="bottomLeft"
      trigger="click"
      @visibleChange="handleVisibleChange"
      :overlayClassName="`${prefixCls}__cloumn-list`"
      :getPopupContainer="getPopupContainer"
    >
      <template #title>
        <div :class="`${prefixCls}__popover-title`">
          <Checkbox
            :indeterminate="indeterminate"
            v-model:checked="checkAll"
            @change="onCheckAllChange"
          >
            {{ t('component.table.settingColumnShow') }}
          </Checkbox>

          <Checkbox
            v-model:checked="checkIndex"
            @change="handleIndexCheckChange"
          >
            {{ t('component.table.settingIndexColumnShow') }}
          </Checkbox>

          <Checkbox
            v-model:checked="checkSelect"
            @change="handleSelectCheckChange"
            :disabled="!defaultRowSelection"
          >
            {{ t('component.table.settingSelectColumnShow') }}
          </Checkbox>

          <a-button size="small" type="link" @click="reset">
            {{ t('common.resetText') }}
          </a-button>
        </div>
      </template>

      <template #content>
        <ScrollContainer>
          <CheckboxGroup
            v-model:value="checkedList"
            @change="onChange"
            ref="columnListRef"
          >
            <template v-for="item in plainOptions" :key="item.value">
              <div
                :class="`${prefixCls}__check-item`"
                v-if="!('ifShow' in item && !item.ifShow)"
              >
                <DragOutlined class="table-column-drag-icon" />
                <Checkbox :value="item.value">
                  {{ item.label }}
                </Checkbox>

                <Tooltip
                  placement="bottomLeft"
                  :mouseLeaveDelay="0.4"
                  :getPopupContainer="getPopupContainer"
                >
                  <template #title>
                    {{ t('component.table.settingFixedLeft') }}
                  </template>
                  <Icon
                    icon="line-md:arrow-align-left"
                    :class="[
                      `${prefixCls}__fixed-left`,
                      {
                        active: item.fixed === 'left',
                        disabled: !checkedList.includes(item.value),
                      },
                    ]"
                    @click="handleColumnFixed(item, 'left')"
                  />
                </Tooltip>
                <Divider type="vertical" />
                <Tooltip
                  placement="bottomLeft"
                  :mouseLeaveDelay="0.4"
                  :getPopupContainer="getPopupContainer"
                >
                  <template #title>
                    {{ t('component.table.settingFixedRight') }}
                  </template>
                  <Icon
                    icon="line-md:arrow-align-left"
                    :class="[
                      `${prefixCls}__fixed-right`,
                      {
                        active: item.fixed === 'right',
                        disabled: !checkedList.includes(item.value),
                      },
                    ]"
                    @click="handleColumnFixed(item, 'right')"
                  />
                </Tooltip>
              </div>
            </template>
          </CheckboxGroup>
        </ScrollContainer>
      </template>
      <SettingOutlined />
    </Popover>
  </Tooltip>
</template>
<script lang="ts">
import type { BasicColumn, ColumnChangeParam } from '../../types/table'
import {
  defineComponent,
  ref,
  reactive,
  toRefs,
  watchEffect,
  nextTick,
  unref,
  computed,
} from 'vue'
import { Tooltip, Popover, Checkbox, Divider } from 'ant-design-vue'
import { SettingOutlined, DragOutlined } from '@ant-design/icons-vue'
import { Icon } from '@/components/Icon'
import { ScrollContainer } from '@/components/Container'
import { useI18n } from '@admin/locale'
import { useTableContext } from '../../hooks/useTableContext'
import { useDesign } from '@/hooks/web/useDesign'
import { isNullOrUndefined } from '@admin/utils'
import { isFunction, cloneDeep, omit } from '@admin/utils'
import { getPopupContainer as getParentContainer } from '@admin/utils'
import Sortablejs from 'sortablejs'
import type Sortable from 'sortablejs'

interface State {
  checkAll: boolean
  isInit?: boolean
  checkedList: string[]
  defaultCheckList: string[]
}

interface Options {
  label: string
  value: string
  fixed?: boolean | 'left' | 'right'
}

export default defineComponent({
  name: 'ColumnSetting',
  components: {
    SettingOutlined,
    Popover,
    Tooltip,
    Checkbox,
    CheckboxGroup: Checkbox.Group,
    DragOutlined,
    ScrollContainer,
    Divider,
    Icon,
  },
  emits: ['columns-change'],

  setup(_, { emit, attrs }) {
    const { t } = useI18n()
    const table = useTableContext()

    const defaultRowSelection = omit(table.getRowSelection(), 'selectedRowKeys')
    let inited = false

    const cachePlainOptions = ref<Options[]>([])
    const plainOptions = ref<Options[] | any>([])

    const plainSortOptions = ref<Options[]>([])

    const columnListRef = ref<ComponentRef>(null)

    const state = reactive<State>({
      checkAll: true,
      checkedList: [],
      defaultCheckList: [],
    })

    const checkIndex = ref(false)
    const checkSelect = ref(false)

    const { prefixCls } = useDesign('basic-column-setting')

    const getValues = computed(() => {
      return unref(table?.getBindValues) || {}
    })

    watchEffect(() => {
      const columns = table.getColumns()
      if (columns.length && !state.isInit) {
        init()
      }
    })

    watchEffect(() => {
      const values = unref(getValues)
      checkIndex.value = !!values.showIndexColumn
      checkSelect.value = !!values.rowSelection
    })

    function getColumns() {
      const ret: Options[] = []
      table
        .getColumns({ ignoreIndex: true, ignoreAction: true })
        .forEach((item) => {
          ret.push({
            label: (item.title as string) || (item.customTitle as string),
            value: (item.dataIndex || item.title) as string,
            ...item,
          })
        })
      return ret
    }

    function init() {
      const columns = getColumns()

      const checkList = table
        .getColumns({ ignoreAction: true })
        .map((item) => {
          if (item.defaultHidden) {
            return ''
          }
          return item.dataIndex || item.title
        })
        .filter(Boolean) as string[]

      if (!plainOptions.value.length) {
        plainOptions.value = columns
        plainSortOptions.value = columns
        cachePlainOptions.value = columns
        state.defaultCheckList = checkList
      } else {
        // const fixedColumns = columns.filter((item) =>
        //   Reflect.has(item, 'fixed')
        // ) as BasicColumn[];

        unref(plainOptions).forEach((item: BasicColumn) => {
          const findItem = columns.find(
            (col: BasicColumn) => col.dataIndex === item.dataIndex,
          )
          if (findItem) {
            item.fixed = findItem.fixed
          }
        })
      }
      state.isInit = true
      state.checkedList = checkList
    }

    // checkAll change
    function onCheckAllChange(e: ChangeEvent) {
      const checkList = plainOptions.value.map((item) => item.value)
      if (e.target.checked) {
        state.checkedList = checkList
        setColumns(checkList)
      } else {
        state.checkedList = []
        setColumns([])
      }
    }

    const indeterminate = computed(() => {
      const len = plainOptions.value.length
      let checkedLen = state.checkedList.length
      unref(checkIndex) && checkedLen--
      return checkedLen > 0 && checkedLen < len
    })

    // Trigger when check/uncheck a column
    function onChange(checkedList: string[]) {
      const len = plainSortOptions.value.length
      state.checkAll = checkedList.length === len
      const sortList = unref(plainSortOptions).map((item) => item.value)
      checkedList.sort((prev, next) => {
        return sortList.indexOf(prev) - sortList.indexOf(next)
      })
      setColumns(checkedList)
    }

    let sortable: Sortable
    let sortableOrder: string[] = []
    // reset columns
    function reset() {
      state.checkedList = [...state.defaultCheckList]
      state.checkAll = true
      plainOptions.value = unref(cachePlainOptions)
      plainSortOptions.value = unref(cachePlainOptions)
      setColumns(table.getCacheColumns())
      sortable.sort(sortableOrder)
    }

    // Open the pop-up window for drag and drop initialization
    function handleVisibleChange() {
      if (inited) return
      nextTick(() => {
        const columnListEl = unref(columnListRef)
        if (!columnListEl) return
        const el = columnListEl.$el as any
        if (!el) return
        // Drag and drop sort
        sortable = Sortablejs.create(unref(el), {
          animation: 500,
          delay: 400,
          delayOnTouchOnly: true,
          handle: '.table-column-drag-icon ',
          onEnd: (evt) => {
            const { oldIndex, newIndex } = evt
            if (
              isNullOrUndefined(oldIndex) ||
              isNullOrUndefined(newIndex) ||
              oldIndex === newIndex
            ) {
              return
            }
            // Sort column
            const columns = cloneDeep(plainSortOptions.value)

            if (oldIndex > newIndex) {
              columns.splice(newIndex, 0, columns[oldIndex])
              columns.splice(oldIndex + 1, 1)
            } else {
              columns.splice(newIndex + 1, 0, columns[oldIndex])
              columns.splice(oldIndex, 1)
            }

            plainSortOptions.value = columns
            setColumns(columns)
          },
        })
        // 记录原始order 序列
        sortableOrder = sortable.toArray()
        inited = true
      })
    }

    // Control whether the serial number column is displayed
    function handleIndexCheckChange(e: ChangeEvent) {
      table.setProps({
        showIndexColumn: e.target.checked,
      })
    }

    // Control whether the check box is displayed
    function handleSelectCheckChange(e: ChangeEvent) {
      table.setProps({
        rowSelection: e.target.checked ? defaultRowSelection : undefined,
      })
    }

    function handleColumnFixed(item: BasicColumn, fixed?: 'left' | 'right') {
      if (!state.checkedList.includes(item.dataIndex as string)) return

      const columns = getColumns() as BasicColumn[]
      const isFixed = item.fixed === fixed ? false : fixed
      const index = columns.findIndex((col) => col.dataIndex === item.dataIndex)
      if (index !== -1) {
        columns[index].fixed = isFixed
      }
      item.fixed = isFixed

      if (isFixed && !item.width) {
        item.width = 100
      }
      table.setCacheColumnsByField?.(item.dataIndex as string, {
        fixed: isFixed,
      })
      setColumns(columns)
    }

    function setColumns(columns: BasicColumn[] | string[]) {
      table.setColumns(columns)
      const data: ColumnChangeParam[] = unref(plainSortOptions).map((col) => {
        const visible =
          columns.findIndex(
            (c: BasicColumn | string) =>
              c === col.value ||
              (typeof c !== 'string' && c.dataIndex === col.value),
          ) !== -1
        return { dataIndex: col.value, fixed: col.fixed, visible }
      })

      emit('columns-change', data)
    }

    function getPopupContainer() {
      return isFunction(attrs.getPopupContainer)
        ? attrs.getPopupContainer()
        : getParentContainer()
    }

    return {
      t,
      ...toRefs(state),
      indeterminate,
      onCheckAllChange,
      onChange,
      plainOptions,
      reset,
      prefixCls,
      columnListRef,
      handleVisibleChange,
      checkIndex,
      checkSelect,
      handleIndexCheckChange,
      handleSelectCheckChange,
      defaultRowSelection,
      handleColumnFixed,
      getPopupContainer,
    }
  },
})
</script>
<style lang="less">
@prefix-cls: ~'@{namespace}-basic-column-setting';

.table-column-drag-icon {
  margin: 0 5px;
  cursor: move;
}

.@{prefix-cls} {
  &__popover-title {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: space-between;
  }

  &__check-item {
    display: flex;
    align-items: center;
    min-width: 100%;
    padding: 4px 16px 8px 0;

    .ant-checkbox-wrapper {
      width: 100%;

      &:hover {
        color: @primary-color;
      }
    }
  }

  &__fixed-left,
  &__fixed-right {
    color: rgb(0 0 0 / 45%);
    cursor: pointer;

    &.active,
    &:hover {
      color: @primary-color;
    }

    &.disabled {
      color: @disabled-color;
      cursor: not-allowed;
    }
  }

  &__fixed-right {
    transform: rotate(180deg);
  }

  &__cloumn-list {
    svg {
      width: 1em !important;
      height: 1em !important;
    }

    .ant-popover-inner-content {
      // max-height: 360px;
      padding-right: 0;
      padding-left: 0;
      // overflow: auto;
    }

    .ant-checkbox-group {
      width: 100%;
      min-width: 260px;
      // flex-wrap: wrap;
    }

    .scrollbar {
      height: 220px;
    }
  }
}
</style>
